﻿#region Using Directives

using System;
using System.IO;
using EnvDTE;
using EnvDTE80;
using System.Collections;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.CodeDom.Compiler;
using System.ComponentModel.Design;
using System.Collections.Generic;
using VSLangProj;
using VsWebSite;
using Microsoft.VisualStudio.Shell.Interop;

#endregion

namespace FeatureBuilder.ContribLib.Helpers
{
    /// <summary>
    /// Provides utility methods for working with the DTE.
    /// </summary>
    public sealed class DteHelper
    {


        #region Fields & Constants

        /// <summary>
        /// Key to access the CurrentWebsiteLanguage property of a project.
        /// </summary>
        private const string CurrentWebsiteLanguagePropertyItem = "CurrentWebsiteLanguage";

        /// <summary>
        /// Value of the CurrentWebsiteLanguage property of a project.
        /// </summary>
        private const string CurrentWebsiteLanguagePropertyValue = "Visual C#";

        #endregion


        /// <summary>
        /// Retrieves the currently selected target in the solution explorer.
        /// </summary>
        /// <remarks>
        /// If there's only one item selected, then the corresponding EnvDTE.Solution, Project 
        /// or ProjectItem element is returned. Otherwise, the <see cref="EnvDTE.SelectedItems"/> 
        /// property is returned.
        /// </remarks>
        /// <exception cref="InvalidOperationException">There's no current selection in the solution explorer.</exception>
        /// <returns>The current selection.</returns>
        public static object GetTarget(_DTE vs)
        {
            Guard.ArgumentNotNull(vs, "vs");

            if (vs.SelectedItems != null && vs.SelectedItems.Count > 0)
            {
                if (vs.SelectedItems.Count == 1)
                {
                    EnvDTE.SelectedItem item = vs.SelectedItems.Item(1);
                    // Determine target to load.
                    if (item.ProjectItem != null)
                    {
                        return item.ProjectItem;
                    }
                    else if (item.Project != null)
                    {
                        return item.Project;
                    }
                    else if (vs.Solution.Properties.Item("Name").Value.Equals(item.Name))
                    {
                        return vs.Solution;
                    }
                    return item;
                }
                else
                {
                    //If more than one element is selected, then assert should deal with the DTE.SelectedItems object
                    return vs.SelectedItems;
                }
            }
            throw new InvalidOperationException(Properties.Resources.DteHelper_NoSelection);
        }

        public static string GetPathFull(_DTE vs, string file)
        {
            Guard.ArgumentNotNull(vs, "vs");
            Guard.ArgumentNotNull(file, "file");

            if (Path.IsPathRooted(file) && !file.StartsWith(Path.GetDirectoryName(vs.Solution.FullName)))
            {
                throw new ArgumentException(String.Format("DteHelper_PathNotRelativeToSln", file, vs.Solution.FullName));
            }
            try
            {
                return Path.Combine(Path.GetDirectoryName(vs.Solution.FullName), file);
            }
            catch
            {
                return file;
            }
        }

        #region BuildPath

        /// <summary>
        /// Builds a path to the element, detecting automatically the 
        /// type of element and building an appropriate path to it.
        /// </summary>
        public static string BuildPath(object toElement)
        {
            Guard.ArgumentNotNull(toElement, "toElement");

            if (toElement is SelectedItem)
            {
                return BuildPath((SelectedItem)toElement);
            }
            else if (toElement is SolutionFolder)
            {
                return BuildPath(((SolutionFolder)toElement).Parent);
            }
            else if (toElement is Project)
            {
                return BuildPath((Project)toElement);
            }
            else if (toElement is ProjectItem)
            {
                return BuildPath((ProjectItem)toElement);
            }
            else
            {
                throw new NotSupportedException(toElement.ToString());
            }
        }

        /// <summary>
        /// Gets an string representation from the current selected item in the solution explorer window.
        /// </summary>
        /// <param name="toSelectedItem"></param>
        /// <returns></returns>
        public static string BuildPath(SelectedItem toSelectedItem)
        {
            Guard.ArgumentNotNull(toSelectedItem, "toSelectedItem");
            if (toSelectedItem.ProjectItem != null)
            {
                return BuildPath(toSelectedItem.ProjectItem);
            }
            else if (toSelectedItem.Project != null)
            {
                return BuildPath(toSelectedItem.Project);
            }

            return toSelectedItem.Name;
        }

        /// <summary>
        /// Gets an string representatio from <paramref name="toProject"/>
        /// </summary>
        //public static string BuildPath(Project toProject)
        //{
        //    Guard.ArgumentNotNull(toProject, "toProject");

        //    string path = "";

        //    // EnvDTE.Solution folders are exposed with the same kind as solution items. 
        //    // This eases compatibility with VS2003 in the future.
        //    if (toProject.Kind == EnvDTE.Constants.vsProjectKindSolutionItems)
        //    {
        //        string folder = "";
        //        foreach (Project project in toProject.DTE.Solution.Projects)
        //        {
        //            folder = project.Name;
        //            // Only build the path if it's not the same top-level project.
        //            if (project == toProject)
        //            {
        //                break;
        //            }
        //            else if (BuildPathToFolder(project, toProject, ref folder))
        //            {
        //                break;
        //            }
        //        }

        //        path = folder + path;
        //    }
        //    else
        //    {
        //        try
        //        {
        //            // Setup projects throw NotImplementedException when queried for ParentProjectItem :S
        //            if (toProject.ParentProjectItem == null)
        //            {
        //                return toProject.Name;
        //            }
        //        }
        //        catch (NotImplementedException) { }

        //        string folder = "";
        //        foreach (Project project in toProject.DTE.Solution.Projects)
        //        {
        //            folder = project.Name;
        //            // Only build the path if it's not the same top-level project.
        //            if (project == toProject)
        //            {
        //                break;
        //            }
        //            else if (BuildPathToFolder(project, toProject, ref folder))
        //            {
        //                break;
        //            }
        //        }

        //        path = folder + path;
        //    }

        //    return path;
        //}

        /// <summary>
        /// Gets an string representation from <paramref name="toItem"/>
        /// </summary>
        /// <param name="toItem"></param>
        /// <returns></returns>
        public static string BuildPath(ProjectItem toItem)
        {
            Guard.ArgumentNotNull(toItem, "toItem");
            string path = "";

            if (toItem.ContainingProject != null)
            {
                if (!BuildPathFromCollection(toItem.ContainingProject.ProjectItems, toItem, ref path))
                {
                    return "";
                }
                else
                {
                    path = Path.Combine(BuildPath(toItem.ContainingProject), path);
                }
            }
            else
            {
                path = toItem.Name;
            }

            return path;
        }

        /// <summary>
        /// Builds a virtual path from an ancestor collection of items to 
        /// a particular instance, which can be any number of levels of nesting 
        /// down the hierarchy.
        /// </summary>
        /// <param name="items">The parent collection of items.</param>
        /// <param name="target">The item to locate in the hierarchy.</param>
        /// <param name="path">The path being built.</param>
        /// <returns>Whether the item was found in the hierarchy of items.</returns>
        public static bool BuildPathFromCollection(ProjectItems items, ProjectItem target, ref string path)
        {
            if (items == null) return false;
            Guard.ArgumentNotNull(target, "target");

            foreach (ProjectItem item in items)
            {
                if (item == target)
                {
                    path = path + target.Name;
                    return true;
                }
                else
                {
                    string tmp = path + item.Name + Path.DirectorySeparatorChar;
                    ProjectItems childitems = item.ProjectItems;
                    if (childitems == null && item.Object is Project)
                        childitems = ((Project)item.Object).ProjectItems;

                    bool found = BuildPathFromCollection(childitems, target, ref tmp);
                    if (found)
                    {
                        path = tmp;
                        return true;
                    }
                }
            }

            return false;
        }

        /// <summary>
        /// Recursively tries to build a path from the parent project to the child one. 
        /// Returns true if the path can be built.
        /// </summary>
        //private static bool BuildPathToFolder(Project parent, Project target, ref string path)
        //{
        //    if (parent == null || parent.ProjectItems == null) return false;

        //    foreach (ProjectItem item in parent.ProjectItems)
        //    {
        //        try
        //        {
        //            if (item.Object == target)
        //            {
        //                path = path + Path.DirectorySeparatorChar + target.Name;
        //                return true;
        //            }
        //            else if (item.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
        //            {
        //                string tmp = path + Path.DirectorySeparatorChar + item.Name;
        //                bool found = BuildPathToFolder(item.Object as Project, target, ref tmp);
        //                if (found)
        //                {
        //                    path = tmp;
        //                    return true;
        //                }
        //            }
        //        }
        //        catch
        //        {
        //            // This is for safety.
        //            // Sometimes there may be invalid items laying around in the solution explorer.
        //            continue;
        //        }
        //    }

        //    return false;
        //}

        #endregion BuildPath

        #region Find methods

        /// <summary>
        /// Retrieves the first project in the solution that matches the specified criteria.
        /// </summary>
        /// <param name="vs">The VS instance.</param>
        /// <param name="match">The predicate condition.</param>
        /// <returns>The project found or <see langword="null"/>.</returns>
        public static Project FindProject(_DTE vs, Predicate<Project> match)
        {
            Guard.ArgumentNotNull(vs, "vs");
            Guard.ArgumentNotNull(match, "match");

            foreach (Project project in vs.Solution.Projects)
            {
                if (match(project))
                {
                    return project;
                }
                else if (project.ProjectItems != null)
                {
                    Project child = FindProjectInternal(project.ProjectItems, match);
                    if (child != null)
                    {
                        return child;
                    }
                }
            }

            return null;
        }

        private static Project FindProjectInternal(ProjectItems items, Predicate<Project> match)
        {
            foreach (ProjectItem item in items)
            {
                Project project = item.Object as Project;
                if (project != null && match(project))
                {
                    return project;
                }
                else if (item.ProjectItems != null)
                {
                    Project child = FindProjectInternal(item.ProjectItems, match);
                    if (child != null)
                    {
                        return child;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// Finds a project in the solution, given its output assembly name.
        /// </summary>
        /// <returns>A <see cref="Project"/> reference or <see langword="null" /> if 
        /// it doesn't exist. Project can be C# or VB.</returns>
        public static Project FindProjectByAssemblyName(_DTE vs, string name)
        {
            return FindProject(vs, delegate(Project project)
            {
                Property prop = project.Properties.Item("AssemblyName");
                return prop != null && prop.Value != null &&
                    prop.Value.ToString() == name;
            });
        }

        /// <summary>
        /// Finds a project in the solution, given its name.
        /// </summary>
        /// <returns>A <see cref="Project"/> reference or <see langword="null" /> if 
        /// it doesn't exist. Project can be C# or VB.</returns>
        public static Project FindProjectByName(_DTE vs, string name)
        {
            return FindProject(vs, delegate(Project project)
            {
                return project.Name == name;
            });
        }

        /// <summary>
        /// Finds the name of the project by.
        /// </summary>
        /// <param name="dte">The DTE.</param>
        /// <param name="name">The name.</param>
        /// <param name="isWeb">if set to <c>true</c> [is web].</param>
        /// <returns></returns>
        public static Project FindProjectByName(DTE dte, string name, bool isWeb)
        {
            Project project = null;

            if (!isWeb)
            {
                project = FindProject(dte, delegate(Project internalProject)
                {
                    return internalProject.Name == name;
                });
            }
            else
            {
                foreach (Project projectTemp in dte.Solution.Projects)
                {
                    if (projectTemp.Name.Contains(name))
                    {
                        project = projectTemp;
                        break;
                    }

                    if (projectTemp.ProjectItems != null)
                    {
                        Project projectTemp1 = FindProjectByName(projectTemp.ProjectItems, name);
                        if (projectTemp1 != null)
                        {
                            project = projectTemp1;
                            break;
                        }
                    }
                }
            }

            return project;
        }


        private static Project FindProjectByName(ProjectItems items, string name)
        {
            foreach (ProjectItem item1 in items)
            {
                if ((item1.Object is Project) && (((Project)item1.Object).Name.Contains(name)))
                {
                    return (item1.Object as Project);
                }

                if (item1.ProjectItems != null)
                {
                    Project project1 = FindProjectByName(item1.ProjectItems, name);
                    if (project1 != null)
                    {
                        return project1;
                    }
                }
            }
            return null;
        }


        /// <summary>
        /// Finds a solution folder in the solution hierarchy, given its 
        /// folder-like location path. 
        /// </summary>
        /// <returns>The solution folder or <see langword="null" /> if 
        /// it doesn't exist.</returns>
        /// <remarks>
        /// Note that this method performs the same work as <see cref="FindProjectByPath"/>, 
        /// but only returns an instance if the <see cref="Project.Object"/> is actually 
        /// an <see cref="EnvDTE80.SolutionFolder"/>. This is because solution folders 
        /// are represented as <see cref="Project"/> elements in Visual Studio.
        /// </remarks>
        public static EnvDTE80.SolutionFolder FindSolutionFolderByPath(EnvDTE.Solution root, string path)
        {
            Guard.ArgumentNotNull(root, "root");
            Guard.ArgumentNotNull(path, "path");
            Project prj = FindProjectByPath(root, path);
            if (prj != null)
            {
                return prj.Object as EnvDTE80.SolutionFolder;
            }
            return null;
        }

        /// <summary>
        /// Finds a project in the solution hierarchy, given its 
        /// folder-like location path. Note that solution folders will 
        /// also be returned, as they are represented as <see cref="Project"/> elements 
        /// in Visual Studio, and the actual folder can be retrieved by casting  
        /// the returned project <see cref="Project.Object"/> property to 
        /// <see cref="EnvDTE80.SolutionFolder"/>.
        /// </summary>
        /// <returns>The project or <see langword="null" /> if 
        /// it doesn't exist.</returns>
        public static Project FindProjectByPath(EnvDTE.Solution root, string path)
        {
            Guard.ArgumentNotNull(root, "root");
            Guard.ArgumentNotNull(path, "path");

            string[] allpaths = path.Split(System.IO.Path.DirectorySeparatorChar,
                System.IO.Path.AltDirectorySeparatorChar);

            if (allpaths.Length == 0)
            {
                return null;
            }

            // First path is the project/solution folder to look into.
            Project prj = null;
            foreach (Project p in root.Projects)
            {
                if (p.Name == allpaths[0])
                {
                    prj = p;
                    break;
                }
            }

            if (prj == null) return null;

            string[] paths = new string[allpaths.Length - 1];
            // If there are no child paths, we reached the end.
            if (paths.Length == 0)
            {
                return prj;
            }

            Array.Copy(allpaths, 1, paths, 0, paths.Length);

            ProjectItem item = FindInCollectionRecursive(prj.ProjectItems, paths, 0);
            if (item == null)
            {
                return null;
            }
            {
                return item.Object as Project;
            }
        }

        /// <summary>
        /// Finds a project item in the received collection, given its name.
        /// </summary>
        /// <param name="collection">The initial collection to start the search.</param>
        /// <param name="name">The name of the item to locate.</param>
        /// <param name="recursive">Specifies whether to search in items collections in turn.</param>
        /// <returns>A <see cref="Project"/> reference or <see langword="null" /> if 
        /// it doesn't exist. Project can be C# or VB.</returns>
        public static ProjectItem FindItemByName(ProjectItems collection, string name, bool recursive)
        {
            Guard.ArgumentNotNull(collection, "collection");
            Guard.ArgumentNotNull(name, "name");
            foreach (ProjectItem item in collection)
            {
                if (item.Name == name)
                    return item;

                // Recurse if specified.
                if (recursive)
                {
                    ProjectItem child = FindItemByName(item.ProjectItems, name, recursive);
                    if (child != null)
                        return child;
                }
            }

            return null;
        }

        /// <summary>
        /// Finds a project item in the solution hierarchy, given its 
        /// folder-like location path. 
        /// </summary>
        /// <returns>The project item or <see langword="null" /> if 
        /// it doesn't exist.</returns>
        /// <remarks>
        /// Note that even projects and solution folders are represented 
        /// as project items, if they are not directly under the solution root, 
        /// but this method checks for the <see cref="ProjectItem.Object"/> property 
        /// to discard matches in this case. If the object to retrieve is a 
        /// project (or solution folder, which is represented as a project too, 
        /// and the folder is retrieved through the <see cref="Project.Object"/> property), 
        /// the <see cref="FindProjectByPath"/> method must be used.
        /// </remarks>
        public static ProjectItem FindItemByPath(EnvDTE.Solution root, string path)
        {
            Guard.ArgumentNotNull(root, "root");
            Guard.ArgumentNotNull(path, "path");

            string[] allpaths = path.Split(
                new char[] {
					System.IO.Path.DirectorySeparatorChar,
					System.IO.Path.AltDirectorySeparatorChar },
                StringSplitOptions.RemoveEmptyEntries);

            if (allpaths.Length == 0)
            {
                return null;
            }

            // First path is the project/solution folder to look into.
            Project prj = null;
            foreach (Project p in root.Projects)
            {
                if (p.Name == allpaths[0])
                {
                    prj = p;
                    break;
                }
            }

            if (prj == null)
            {
                return null;
            }

            string[] paths = new string[allpaths.Length - 1];
            // If there are no child paths, this is not an item but the project itself.
            if (paths.Length == 0)
            {
                return null;
            }

            Array.Copy(allpaths, 1, paths, 0, paths.Length);

            ProjectItem item = FindInCollectionRecursive(prj.ProjectItems, paths, 0);
            if ((item != null) && !(item.Object is Project || item.Object is EnvDTE80.SolutionFolder))
            {
                // Only return the item if it's not a container for a Project or SolutionFolder.
                return item;
            }
            return null;
        }

        /// <summary>
        /// Finds an item in a collection using its path-like notation inside the hierarchy of 
        /// <see cref="ProjectItems"/>.
        /// </summary>
        public static ProjectItem FindInCollection(ProjectItems collection, string path)
        {
            string[] allpaths = path.Split(
                new char[] {
					System.IO.Path.DirectorySeparatorChar,
					System.IO.Path.AltDirectorySeparatorChar },
                StringSplitOptions.RemoveEmptyEntries);

            return FindInCollectionRecursive(collection, allpaths, 0);
        }

        private static ProjectItem FindInCollectionRecursive(ProjectItems collection, string[] paths, int index)
        {
            foreach (ProjectItem item in collection)
            {
                if (item.Name == paths[index] ||
                    MatchesWebProjectName(item, paths[index]))
                {
                    if (index == paths.Length - 1)
                    {
                        // We reached the item we were looking for.
                        return item;
                    }
                    else
                    {
                        // Otherwise, keep processing.
                        // If item is a project/solution folder, cast before moving on.
                        if (item.Object is Project)
                        {
                            return FindInCollectionRecursive(
                                ((Project)item.Object).ProjectItems,
                                paths, ++index);
                        }
                        else
                        {
                            return FindInCollectionRecursive(item.ProjectItems, paths, ++index);
                        }
                    }
                }
            }

            // Item wasn't found.
            return null;
        }

        private static bool MatchesWebProjectName(ProjectItem item, string name)
        {
            Project project = item.Object as Project;
            if (project != null && project.Kind == VsWebSite.PrjKind.prjKindVenusProject)
            {
                string simpleName = Path.GetDirectoryName(item.Name);
                simpleName = simpleName.Substring(simpleName.LastIndexOf(Path.DirectorySeparatorChar) + 1);
                if (name == simpleName)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// UIHierarchy.GetItem does not always work :S, so I do it by hand here.
        /// </summary>
        private static UIHierarchyItem FindHierarchyItemByPath(UIHierarchyItems items, string[] paths, int index, Dictionary<UIHierarchyItems, bool> expandedItems)
        {
            foreach (UIHierarchyItem item in items)
            {
                if (item.Name == paths[index])
                {
                    if (index == paths.Length - 1)
                    {
                        // We reached the item we were looking for.
                        return item;
                    }
                    else
                    {
                        // Otherwise, keep processing.
                        return FindHierarchyItemByPath(item.UIHierarchyItems, paths, ++index, expandedItems);
                    }
                }
            }

            expandedItems.Add(items, items.Expanded);

            // Item wasn't found.
            return null;
        }

        #endregion Find methods

        #region IsXXX methods

        /// <summary>
        /// Determines whether the project is a web project.
        /// </summary>
        public static bool IsWebProject(Project project)
        {
            Guard.ArgumentNotNull(project, "project");
            return project.Kind == VsWebSite.PrjKind.prjKindVenusProject;
        }

        /// <summary>
        /// Determines if the project item is a web reference.
        /// </summary>
        public static bool IsWebReference(ProjectItem item)
        {
            Guard.ArgumentNotNull(item, "item");

            if (item.ContainingProject.Object is VSProject)
            {
                ProjectItem webrefs = ((VSProject)item.ContainingProject.Object).WebReferencesFolder;
                if (webrefs != null && webrefs.ProjectItems != null)
                {
                    foreach (ProjectItem webref in webrefs.ProjectItems)
                    {
                        if (webref == item)
                        {
                            return true;
                        }
                    }
                    return false;
                }
                return false;
            }
            return false;
        }

        /// <summary>
        /// Performs the validation of the item passed as target
        /// Returns true if the reference is allowed to be executed in the target
        /// that is if the target is a web project and C# project
        /// </summary>
        /// <param name="target">The target.</param>
        /// <returns>
        /// 	<c>true</c> if [is web C sharp project] [the specified target]; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsWebCSharpProject(object target)
        {
            Project project = null;
            if (target is Project)
            {
                project = (Project)target;
            }
            else if (target is ProjectItem)
            {
                project = ((ProjectItem)target).ContainingProject;
            }

            if (project != null &&
                DteHelper.IsWebProject(project) &&
                project.Properties != null)
            {
                try
                {
                    Property property = project.Properties.Item(CurrentWebsiteLanguagePropertyItem);
                    return (property.Value != null &&
                        property.Value.ToString().Equals(CurrentWebsiteLanguagePropertyValue, StringComparison.InvariantCultureIgnoreCase));
                }
                catch (Exception exception)
                {
                    Trace.TraceError(exception.ToString());
                    // Some Project implementations throws this excpetion (i.e.: Analysis services project)
                    // or the CurrentWebsiteLanguagePropertyItem property does not exists.
                    return false;
                }
            }

            return false;
        }

        #endregion IsXXX methods

        #region Get Methods

        /// <summary>
        /// Retrieves the project currently selected, if any.
        /// </summary>
        public static Project GetSelectedProject(_DTE vs)
        {
            Guard.ArgumentNotNull(vs, "vs");

            foreach (object obj in (object[])vs.ActiveSolutionProjects)
            {
                if (obj is Project) return obj as Project;
            }

            return null;
        }

        /// <summary>
        /// Gets the code DOM provider from the language of the specified project.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <returns></returns>
        public static CodeDomProvider GetCodeDomProvider(Project project)
        {
            CodeDomProvider codeDom = CodeDomProvider.CreateProvider("C#");
            if (project != null)
            {
                string defaultExtension = GetDefaultExtension(project);
                string Language = CodeDomProvider.GetLanguageFromExtension(defaultExtension);
                codeDom = CodeDomProvider.CreateProvider(Language);
            }

            return codeDom;
        }


        /// <summary>
        /// Retrieves the code file extension for the project.
        /// </summary>
        /// <param name="project">The project.</param>
        /// <returns></returns>
        public static string GetDefaultExtension(Project project)
        {
            bool isWebProject = DteHelper.IsWebProject(project);
            string defaultExtension = ".cs";
            if (isWebProject)
            {
                defaultExtension = IsWebCSharpProject(project) ? ".cs" : ".vb";
            }
            return defaultExtension;
        }

        /// <summary>
        /// Projects the exists.
        /// </summary>
        /// <param name="solution">The solution.</param>
        /// <param name="projectName">Name of the project.</param>
        /// <param name="language">The language.</param>
        /// <returns></returns>
        public static bool ProjectExists(Solution solution, string projectName, LanguageType language)
        {
            bool exists = false;

            string solutionPath = Path.GetDirectoryName(
                            solution.Properties.Item("Path").Value.ToString());

            if (Directory.Exists(solutionPath))
            {
                string projectFile = string.Concat(projectName, GetProjectExtension(language));

                exists = (Directory.GetFiles(
                            solutionPath,
                            projectFile,
                            SearchOption.AllDirectories).Length > 0);
            }

            return exists;
        }

        private static string GetProjectExtension(LanguageType language)
        {
            switch (language)
            {
                case LanguageType.cs:
                    return ".csproj";
                case LanguageType.vb:
                    return ".vbproj";
                default:
                    return ".csproj";
            }
        }

        /// <summary>
        /// Gets the current selected IVsHierarchy.
        /// </summary>
        /// <returns></returns>
        public static IVsHierarchy GetCurrentSelection(System.IServiceProvider provider)
        {
            Guard.ArgumentNotNull(provider, "provider");
            uint pitemid = 0;
            return GetCurrentSelection(provider, out pitemid);
        }


        /// <summary>
        /// Gets the current selected IVsHierarchy.
        /// </summary>
        public static IVsHierarchy GetCurrentSelection(System.IServiceProvider provider, out uint pitemid)
        {
            Guard.ArgumentNotNull(provider, "provider");

            IVsMonitorSelection pSelection =
                (IVsMonitorSelection)provider.GetService(typeof(SVsShellMonitorSelection));
            IntPtr ptrHierchary = IntPtr.Zero;
            IVsMultiItemSelect ppMIS = null;
            IntPtr ppSC = IntPtr.Zero;
            pSelection.GetCurrentSelection(out ptrHierchary, out pitemid, out ppMIS, out ppSC);
            if (ptrHierchary != IntPtr.Zero)
            {
                return (IVsHierarchy)Marshal.GetObjectForIUnknown(ptrHierchary);
            }
            else // There is not selection, so let's return the solution
            {
                IVsHierarchy solution = (IVsHierarchy)provider.GetService(typeof(SVsSolution));
                pitemid = __VSITEMID.ROOT;
                return solution;
            }
        }

        internal sealed class __VSITEMID
        {
            // Fields
            public const uint NIL = 0xFFFFFFFF;
            public const uint ROOT = 0xFFFFFFFE;
            public const uint SELECTION = 0xFFFFFFFD;
        }




        #endregion

    }
}
